
#include <QtXml/QDomDocument>

#include "mymainwindow.h"
#include "openglframe.h"
#include "typedefs.h"

#include <iostream>

using namespace std;

void printInfoAndExit() {
   
   cout<<endl;
   cout<<"Usage: qtEasyAnimator filename.{xml}"<<endl;
   exit(0);
}

/// Constructor.
MyMainWindow::MyMainWindow(QMainWindow *parent): QMainWindow(parent) {
   
   // Make sure the default opengl format includes stencil buffer and alpha.
   QGLFormat fmt;
   fmt.setAlpha(true);
   fmt.setStencil(true);
   QGLFormat::setDefaultFormat(fmt);
   
   // Executes the method which builds the remaining widgets of the window.
   // (Inherited from Ui::MainWindow)
   setupUi(this);
   
   /// Make the same glContext common for the two views (glFrameMocap, glFrameModel)
   //~ if (glFrameModel != 0) { delete glFrameModel; }
   //~ glFrameModel = new GLMeshModelFrame(splitter, glFrameMocap);
   
   timer = new QTimer(this);
   
   //~ connect(actionZoomIn, SIGNAL(triggered()), this->glFrameMocap, SLOT(zoomIn()));
   //~ connect(actionZoomOut, SIGNAL(triggered()), this->glFrameMocap, SLOT(zoomOut()));
   //~ connect(actionZoomAll, SIGNAL(triggered()), this->glFrameMocap, SLOT(zoomAll()));
   connect(actionZoomIn, SIGNAL(triggered()), this->glFrameModel, SLOT(zoomIn()));
   connect(actionZoomOut, SIGNAL(triggered()), this->glFrameModel, SLOT(zoomOut()));
   connect(actionZoomAll, SIGNAL(triggered()), this->glFrameModel, SLOT(zoomAll()));
   
   connect(slider, SIGNAL(sliderPressed()), this, SLOT(setMaximumValueSlider()));
   connect(slider, SIGNAL(valueChanged(int)), this, SLOT(animateValueSlider(int)));
   
   //~ // groupping exclusives actions 
   meshrenderGroup = new QActionGroup(this);
   meshrenderGroup->addAction(actionRenderPoints);
   meshrenderGroup->addAction(actionRenderWireframe);
   meshrenderGroup->addAction(actionRenderHiddenLines);
   meshrenderGroup->addAction(actionRenderFlatLines);
   meshrenderGroup->addAction(actionRenderFlat);
   meshrenderGroup->addAction(actionRenderSmooth);
   
   defMethodGroup = new QActionGroup(this);
   defMethodGroup->addAction(actionMlsBlending);
   defMethodGroup->addAction(actionRbfClassic);
   defMethodGroup->addAction(actionRbfGeodesic);
   defMethodGroup->addAction(actionMlsCtrlPointsEuclidian);
   defMethodGroup->addAction(actionMlsCtrlPointsGeodesic);
   defMethodGroup->addAction(actionMlsCtrlSegmentsEuclidian);
   defMethodGroup->addAction(actionMlsCtrlSegmentsGeodesic);
   defMethodGroup->addAction(actionRbfClassic);
   defMethodGroup->addAction(actionRbfGeodesic);
   defMethodGroup->addAction(actionAffineTransformations);
   
   actionMlsBlending->setChecked(true);
   //~ glFrameModel->setCurrentSkelView(glFrameMocap);
}

void MyMainWindow::open(int argc, char *argv[]) {
   
   if (argc != 2) printInfoAndExit();
   
   QString filename(argv[1]);
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   if (extension == "xml") 
      open_xmlfile(filename);
   else 
      printInfoAndExit();
}

void MyMainWindow::open_xmlfile(QString filename) {
   
   QFileInfo fi(filename);
   QString filepath = fi.absolutePath() + '/';
   //~ cout<<"fi.absolutePath() "<<fi.absolutePath().toStdString()<<endl;
   //~ cout<<"open_xmlfile>>>> "<<filename.toStdString().c_str()<<endl;
   /// ///////////////
   QDomDocument doc("AnimML");
   QFile file(filename);
   if (!file.open(QIODevice::ReadOnly))
      return;
   if (!doc.setContent(&file)) {
      file.close();
      return;
   }
   file.close();
   
   // print out the element names of all elements that are direct children
   // of the outermost element.
   QDomElement docElem = doc.documentElement();
   QDomNode dn = docElem.firstChild();
   while(!dn.isNull()) {
      QDomElement e = dn.toElement(); // try to convert the node to an element.
      if(!e.isNull()) {
         //~ cout << e.tagName().toStdString() << endl; // the node really is an element.
         //~ cout << e.attribute("meshfname").toStdString() << endl;
         //~ cout << e.attribute("skelfname").toStdString() << endl;
         //~ cout << e.attribute("skinfname").toStdString() << endl;
         //~ cout << e.attribute("motionfname").toStdString() << endl;
         
         if (not e.attribute("meshfname").isEmpty())
            glFrameModel->loadShape(filepath + e.attribute("meshfname")); 
         if (not e.attribute("skefname").isEmpty())
            glFrameModel->loadSkeleton(filepath + e.attribute("skefname")); 
         if (not e.attribute("sojfname").isEmpty())
            glFrameModel->loadSkinningOnJoints(filepath + e.attribute("sojfname"));
         if (not e.attribute("sobfname").isEmpty())
            glFrameModel->loadSkinningOnBones(filepath + e.attribute("sobfname"));
         if (not e.attribute("asffname").isEmpty())
            glFrameModel->loadSkeleton(filepath + e.attribute("asffname"));
         if (not e.attribute("amcfname").isEmpty())
            glFrameModel->loadMotion(filepath + e.attribute("amcfname"));
         if (not e.attribute("cbfname").isEmpty())
            glFrameModel->loadClosestBone(filepath + e.attribute("cbfname"));
         if (not e.attribute("cjfname").isEmpty())
            glFrameModel->loadClosestJoint(filepath + e.attribute("cjfname"));
         
         //~ QStringList tl = e.attribute("translation").split(" ", QString::SkipEmptyParts);
         //~ if (tl.size() == 3) {
            //~ float t[]={tl.at(0).toDouble(), tl.at(1).toDouble(), tl.at(2).toDouble()};
            //~ glFrame->loadTranslation(t);
         //~ }
         //~ else {
            //~ float t[]={0,0,0};
            //~ glFrame->loadTranslation(t);
         //~ }
      }
      dn = dn.nextSibling();
   }
   //~ glFrame->buildAnimators();
}

// Tries to load file if filename not empty.
void MyMainWindow::openMeshModel(QString filename) {
   
   if (filename != "") {
      QFileInfo fi(filename);
      QString extension = fi.suffix();
      
      if (extension != "off") {
         QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" file extension is not supported. \nYou must open < off > format models.";
         QMessageBox::critical(this, tr("Opening Error"), errorMsgFormat.arg(filename, extension));
         return;
      }
      
      if (not glFrameModel->loadShape(filename)) {
         QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" format is incorrect";
         QMessageBox::critical(this, tr("Opening Error"), errorMsgFormat.arg(filename, extension));
         return;
      }
      
      filenamemodel = filename;
      this->setWindowTitle("qtMocapViewer - " + filenamemodel);
   }
}

// Tries to load file if filename not empty.
void MyMainWindow::openSkeleton(QString filename) {
   
   if (filename != "") {
      QFileInfo fi(filename);
      QString extension = fi.suffix();
      
      if (extension != "ske" and extension != "asf") {
         QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" file extension is not supported. \nYou must open < ske|asf > format models.";
         QMessageBox::critical(this, tr("Opening Error"), errorMsgFormat.arg(filename, extension));
         return;
      }
      
      if (not glFrameModel->loadSkeleton(filename)) {
         QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" format is incorrect";
         QMessageBox::critical(this, tr("Opening Error"), errorMsgFormat.arg(filename, extension));
         return;
      }
      
      filenamemodel = filename;
      this->setWindowTitle(" - " + filenamemodel);
   }
}

/// Automatically bound to the actionOpen menu item of the main window 
/// by the designer mechanism. Pops up a file open dialog for selecting
/// an "off" file.
void MyMainWindow::on_actionOpenMeshModel_triggered() {
   
    // Asks for a file name to open.
    QString filename = QFileDialog::getOpenFileName (
                      this, 
                      tr("Choose a file"),
                      ".",
                      tr("Off meshes (*.off)"));
    
    //~ // Tries to load file if filename not empty.
    if (filename != "") {
      openMeshModel(filename);
      glFrameModel->updateGL();
    }
}

void MyMainWindow::on_actionLoadDeformedSkeleton_triggered() {
   
    // Asks for a file name to open.
    QString filename = QFileDialog::getOpenFileName (
                      this, 
                      tr("Choose a file"),
                      ".",
                      tr("ske skeletons (*.ske)"));
    
    //~ // Tries to load file if filename not empty.
    if (filename != "") {
      glFrameModel->loadDeformedSkeleton(filename);
      glFrameModel->updateGL();
    }
}

void MyMainWindow::on_actionLoadClosestBone_triggered() {
   
    // Asks for a file name to open.
    QString filename = QFileDialog::getOpenFileName (
                      this, 
                      tr("Choose a file"),
                      ".",
                      tr("Closest bone files (*.cb)"));
    
    //~ // Tries to load file if filename not empty.
    if (filename != "") {
      glFrameModel->loadClosestBone(filename);
      glFrameModel->updateGL();
    }
}

void MyMainWindow::on_actionLoadSkinningOnJoints_triggered() {
   
    // Asks for a file name to open.
    QString filename = QFileDialog::getOpenFileName (
                      this, 
                      tr("Choose a file"),
                      ".",
                      tr("Skinning on joints files (*.soj)"));
    if (filename != "") {
      glFrameModel->loadSkinningOnJoints(filename);
      glFrameModel->updateGL();
    }
}

void MyMainWindow::on_actionLoadSkinningOnBones_triggered() {
   
    QString filename = QFileDialog::getOpenFileName (
                      this, 
                      tr("Choose a file"),
                      ".",
                      tr("Skinning on bones files (*.sob)"));
    if (filename != "") {
      glFrameModel->loadSkinningOnBones(filename);
      glFrameModel->updateGL();
    }
}

void MyMainWindow::on_actionRenderPoints_triggered() {
   glFrameModel->meshRenderMode(POINTS);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRenderWireframe_triggered() {
   glFrameModel->meshRenderMode(WIREFRAME);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRenderFlat_triggered() {
   glFrameModel->meshRenderMode(FLAT);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRenderFlatLines_triggered() {
   glFrameModel->meshRenderMode(FLATLINES);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRenderHiddenLines_triggered() {
   glFrameModel->meshRenderMode(HIDDENLINES);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRenderSmooth_triggered() {
   glFrameModel->meshRenderMode(SMOOTH);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionLightOnOff_triggered() {
   if (actionLightOnOff->isChecked())
      actionLightOnOff->setIcon(QIcon(QString::fromUtf8("icons/lighton.png")));
   else 
      actionLightOnOff->setIcon(QIcon(QString::fromUtf8("icons/lightoff.png")));
   glFrameModel->lightingOnOff(actionLightOnOff->isChecked());
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionProjection_triggered() {
   
   if (glFrameModel->isProjectionOrtho()) {
      const QString qstring("Perspective");
      actionProjection->setText(qstring);
      actionProjection->setIcon(QIcon(QString::fromUtf8("icons/perspective.png")));
      //~ glFrameMocap->orthographic(false);
      glFrameModel->orthographic(false);
   }
   else {
      const QString qstring("Orthographic");
      actionProjection->setText(qstring);
      actionProjection->setIcon(QIcon(QString::fromUtf8("icons/orthographic.png")));
      //~ glFrameMocap->orthographic(true);
      glFrameModel->orthographic(true);
   }
   
   //~ glFrameMocap->setGLview();
   //~ glFrameMocap->updateGL();
   
   glFrameModel->setGLview();
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewModel_triggered() {
   
   if (actionViewModel->isChecked())
      glFrameModel->viewItemOn(MODELDEF);
   else 
      glFrameModel->viewItemOff(MODELDEF);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewFloor_triggered() {
   
   if (actionViewFloor->isChecked()) {
      //~ glFrameMocap->viewItemOn(FLOOR);
      glFrameModel->viewItemOn(FLOOR);
   }
   else {
      //~ glFrameMocap->viewItemOff(FLOOR);
      glFrameModel->viewItemOff(FLOOR);
   }
   //~ glFrameMocap->updateGL();
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewInfo_triggered() {
   
   if (actionViewInfo->isChecked()) {
      //~ glFrameMocap->viewItemOn(INFO);
      glFrameModel->viewItemOn(INFO);
   }
   else {
      //~ glFrameMocap->viewItemOff(INFO);
      glFrameModel->viewItemOff(INFO);
   }
   //~ glFrameMocap->updateGL();
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewRigging_triggered() {
   glFrameModel->meshRigSkinMode(RIGGING);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewSkinning_triggered() {
   glFrameModel->meshRigSkinMode(SKINNING);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewRiggingOnBones_triggered() {
   glFrameModel->meshRigSkinMode(RIGGINGONBONES);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewRiggingEdges_triggered() {
   glFrameModel->meshRigSkinMode(RIGGINGEDGES);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewRiggingEdgesOnBones_triggered() {
   glFrameModel->meshRigSkinMode(RIGGINGEDGESONBONES);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionNoViewRigSkin_triggered() {
   glFrameModel->meshRigSkinMode(NORIGSKIN);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewSkeleton_triggered() {
   
   if (actionViewSkeleton->isChecked())
      glFrameModel->viewItemOn(SKELETONINI);
   else 
      glFrameModel->viewItemOff(SKELETONINI);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionViewSkeletonDef_triggered() {
   
   if (actionViewSkeletonDef->isChecked())
      glFrameModel->viewItemOn(SKELETONDEF);
   else 
      glFrameModel->viewItemOff(SKELETONDEF);
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionAlignment_triggered() {
   
   glFrameModel->skeletonAlignment();
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionFitting_triggered() {
   
   glFrameModel->skeletonFitting();
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionDeformation_triggered() {
   
   glFrameModel->meshDeformation();
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionTransferMode_triggered() {
   
   if (actionTransferMode->isChecked()) {
      glFrameModel->setInteractionMode(SELECT_CTRLPOINTS_MODE);
      glFrameModel->buildCtrlPoints();
   }
   else {
      glFrameModel->setInteractionMode(VIEW_MODE);
   }
   glFrameModel->updateGL();
}

void MyMainWindow::setMaximumValueSlider() {
   
   slider->setMaximum(glFrameModel->nMaxofFrames());
}

void MyMainWindow::animateValueSlider(int val) {
   
   //~ glFrameMocap->animateFrame(val-1);
   glFrameModel->animateFrame(val-1);
}

void MyMainWindow::addValueSlider() {
   
   if (slider->value() < slider->maximum())
      slider->setValue(slider->value()+1);
   else
      on_bStop_clicked();
}

void MyMainWindow::on_bPlay_clicked() {
   
   slider->setMaximum(glFrameModel->nMaxofFrames());
   
   connect(timer, SIGNAL(timeout()), this, SLOT(addValueSlider()));
   timer->start(0);
}

void MyMainWindow::on_bStop_clicked() {
   
   slider->setValue(0);
   disconnect(timer, 0, 0, 0);
}

void MyMainWindow::on_bPause_clicked() {
   
   disconnect(timer, 0, 0, 0);
}

void MyMainWindow::on_actionResetOperations_triggered() {
   
   glFrameModel->resetOperations();
}

void MyMainWindow::on_actionMlsBlending_triggered() {
   glFrameModel->setDefMethod("MLS_BLENDING");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionMlsCtrlPointsEuclidian_triggered() {
   glFrameModel->setDefMethod("MLS_CTRLPOINTS_EUCL");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionMlsCtrlPointsGeodesic_triggered() {
   glFrameModel->setDefMethod("MLS_CTRLPOINTS_GEOD");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionMlsCtrlSegmentsEuclidian_triggered() {
   glFrameModel->setDefMethod("MLS_CTRLSEGMENTS_EUCL");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionMlsCtrlSegmentsGeodesic_triggered() {
   glFrameModel->setDefMethod("MLS_CTRLSEGMENTS_GEOD");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRbfClassic_triggered() {
   glFrameModel->setDefMethod("RBF_CLASSIC");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionRbfGeodesic_triggered() {
   glFrameModel->setDefMethod("RBF_GEODESIC");
   glFrameModel->updateGL();
}

void MyMainWindow::on_actionAffineTransformations_triggered() {
   glFrameModel->setDefMethod("AFFINE_TRANSFORMATIONS");
   glFrameModel->updateGL();
}
